home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / pluginy Firefox / 722 / 722.xpi / chrome / noscript.jar / content / noscript / noscriptOverlay.js < prev    next >
Text File  |  2010-02-12  |  72KB  |  2,226 lines

  1. var noscriptOverlay = (function() {
  2.  
  3. var $ = function(id) { return document.getElementById(id) };
  4. const CC = Components.classes;
  5. const CI = Components.interfaces;
  6.  
  7.  
  8. return noscriptUtil.service ? {
  9.  
  10.   ns: noscriptUtil.service,
  11.   
  12.   getString: function(key, parms) {
  13.     return noscriptUtil.getString(key, parms);
  14.   }
  15. ,
  16.   toggleCurrentPage: function(forceLevel) {
  17.     const ns = this.ns;
  18.     var level = ns.getPref("toolbarToggle", 3) || forceLevel;
  19.     if (!level) return false;
  20.     
  21.     const url = ns.getQuickSite(content.document.documentURI, level);
  22.     if (url)
  23.       this.safeAllow(url, !ns.isJSEnabled(url), ns.getPref("toggle.temp"));
  24.     
  25.     return true;
  26.   },
  27.  
  28.   
  29.   getSites: function() {
  30.     return this.ns.getSites(this.currentBrowser);
  31.   },
  32.   
  33.   get prompter() {
  34.     delete this.prompter;
  35.     return this.prompter = noscriptUtil.prompter;
  36.   },
  37.   
  38.   openPopup: function(ev) {
  39.     var parent = ev.currentTarget;
  40.     var popupId = parent.getAttribute("context");
  41.    
  42.     if (this.stickyUI) {
  43.       if (popupId) {
  44.         parent.setAttribute("popup", popupId);
  45.         parent.removeAttribute("context");
  46.         parent.click();
  47.       }
  48.       return;
  49.     }
  50.     
  51.     ev.preventDefault();
  52.     if (!popupId) {
  53.       var popup = document.firstChild;
  54.       if (popup && this.isOpenOrJustClosed(popup)) {
  55.         popup.hidePopup();
  56.         popup._lastClosed = 0;
  57.         this._currentPopup = null;
  58.         return;
  59.       }
  60.     }
  61.     var pb = parent.boxObject;
  62.     var ctxEv = document.createEvent("MouseEvents");
  63.     ctxEv.initMouseEvent("contextmenu", true, true, window, 0,
  64.         pb.screenX, pb.screenY, 0, 0, false, false, false, false, 2, null);
  65.     parent.dispatchEvent(ctxEv);
  66.   },
  67.   onContextMenu: function(ev) {
  68.     var parent = ev.currentTarget;
  69.     var popup = parent.firstChild;
  70.     if (!(popup && popup.showPopup)) return;
  71.     if (this.stickyUI) {
  72.       popup._context = true;
  73.     }
  74.     ev.preventDefault();
  75.     popup.showPopup();
  76.   },
  77.   
  78.   onMenuShowing: function(ev, noSticky) {
  79.     
  80.     var popup = ev.currentTarget;
  81.   
  82.     if (popup != ev.originalTarget) return;
  83.    
  84.     var stickyUI = this.stickyUI;
  85.     
  86.     if (stickyUI) {
  87.       popup._context = popup._context || popup.parentNode && popup.parentNode.getAttribute("context") == popup.id;
  88.       
  89.       popup.setAttribute("sticky", !noSticky &&
  90.        (popup == stickyUI ||
  91.         !popup._context && this.useStickyUI));
  92.       
  93.       popup._context =  false;
  94.     } else popup.removeAttribute("sticky");
  95.     
  96.     popup.addEventListener("popuphidden", function(ev) { noscriptOverlay.onMenuHidden(ev) }, false);
  97.     
  98.     popup.style.visibility = "hidden"; // work-around for bug 4066046
  99.     popup.addEventListener("popupshown", noscriptOverlay.onMenuShown, false);
  100.                            
  101.     this.prepareMenu(popup);
  102.   },
  103.   onMenuShown: function(ev) {
  104.     ev.currentTarget.style.visibility = "";
  105.   },
  106.   
  107.   _reloadDirty: false,
  108.   
  109.   isOpenOrJustClosed: function(popup) {
  110.     return popup.state && popup.state == "open" ||
  111.       this._currentPopup == popup ||
  112.       (new Date() - (popup._lastClosed || 0)) < 300;
  113.   },
  114.   
  115.   onMenuHidden: function(ev) {
  116.     var popup = ev.currentTarget;
  117.     if (ev.originalTarget != popup) return;
  118.     popup.removeEventListener(ev.type, arguments.callee, false);
  119.     
  120.     if (this._reloadDirty && !this.liveReload) {
  121.       this.ns.reloadWhereNeeded();
  122.       this.ns.savePrefs();
  123.     }
  124.     
  125.     if (popup.id == "noscript-tbb-popup") {
  126.       // take back our stuff
  127.        this._currentPopup = null;
  128.       noscriptOverlay.prepareMenu($("noscript-status-popup"));
  129.      
  130.     }
  131.     popup._lastClosed = Date.now();
  132.     this._reloadDirty = false;
  133.     this._currentPopup = null;
  134.   },
  135.  
  136.   prepareContextMenu: function(ev) {
  137.     var menu = $("noscript-context-menu");
  138.     if (this.ns.getPref("ctxMenu", true)) {
  139.       menu.removeAttribute("hidden");
  140.     } else {
  141.       menu.setAttribute("hidden", "true");
  142.       return;
  143.     }
  144.     this.updateStatusClass(menu);
  145.     menu.setAttribute("tooltiptext", this.statusIcon.getAttribute("tooltiptext"));
  146.   }
  147.   
  148. ,
  149.   toggleMenuOpt: function(node) {
  150.     var val=node.getAttribute("checked")=="true";
  151.     var k=node.id.lastIndexOf("-opt-");
  152.     if (k>-1) {
  153.       this.ns.setPref(node.id.substring(5+k),val);
  154.     }
  155.     return val;
  156.   }
  157. ,
  158.  
  159.   prepareOptItems: function(popup) {
  160.     const notifications = this.getNotificationBox();
  161.     const opts = popup.getElementsByAttribute("type", "checkbox");
  162.     var k, j, node, id;
  163.     for (j = opts.length; j-- > 0;) {
  164.       node = opts[j];
  165.       var id = node.id;
  166.       if ((k = id.lastIndexOf("-opt-")) > -1) {
  167.         if ((!notifications) && id.indexOf("notification") - 1) {
  168.           node.setAttribute("hidden", "true");
  169.         } else {
  170.           node.setAttribute("checked", this.ns.getPref(node.id.substring(5 + k)));
  171.         }
  172.       }
  173.     }
  174.   },
  175.   
  176.   
  177.   prepareXssMenu: function(popup, invert) {
  178.     this.prepareOptItems(this.populateXssMenu(popup, invert));
  179.   },
  180.   populateXssMenu: function(popup, invert) {
  181.     var ref = $("noscript-mi-xss-unsafe-reload");
  182.     var parent = ref.parentNode;
  183.     var inverse = parent.lastChild.id != "noscript-mi-xss-faq";
  184.     invert = inverse && !invert;
  185.     if (parent != popup) {
  186.       while (parent.firstChild) {
  187.         popup.appendChild(invert ? parent.lastChild : parent.firstChild);
  188.       }
  189.     } else if (invert) {
  190.       for (var p, n = parent.lastChild; n; n = p) {
  191.         p = n.previousSibling;
  192.         parent.appendChild(n);
  193.       }
  194.     }
  195.     return popup;
  196.   },
  197.   
  198.   revokeTooltip: function(tempSites) {
  199.     const ns = this.ns;
  200.     const fx3 = typeof(/ /) == "object";
  201.     var sep = fx3 ? "\n\n" : " | ";
  202.  
  203.      
  204.     // remove http/https/file CAPS hack entries
  205.     var tip = "<SCRIPT>: ";
  206.     if (tempSites) {
  207.       tempSites = this.ns.siteUtils.sanitizeString(tempSites.replace(/\b(?:https?|file):\/\//g, "")).split(/\s+/);
  208.       tip += (fx3 // Fx 3, multiline tooltips
  209.           ? tempSites.join(", ")
  210.           : tempSites.length);
  211.     } else tip += "0";
  212.     
  213.     var len = ns.objectWhitelistLen;
  214.     if (len) tip += sep + "<OBJECT>: " + len;
  215.     
  216.     len = ns.clearClickHandler && ns.clearClickHandler.whitelistLen;
  217.     if (len) tip += sep + "ClearClick: " + len;
  218.     
  219.     return tip;
  220.   },
  221.   
  222.   
  223.   initPopups: function() {
  224.     var sticky = this.stickyUI; // early init
  225.     var popup = $("noscript-status-popup");
  226.     // copy status bar menus
  227.     ["noscript-statusIcon", "noscript-statusLabel"].forEach(function(id) {
  228.       var parent = $(id);
  229.       if (!parent) return;
  230.       if (parent.firstChild && /popup/.test(parent.firstChild.tagName)) return;
  231.       var clone = popup.cloneNode(true);
  232.       clone.id  = parent.id + "-popup";
  233.       parent.insertBefore(clone, parent.firstChild);
  234.       if (!sticky) clone._context = true;
  235.     });
  236.   },
  237.   
  238.   _currentPopup: null,
  239.   
  240.   prepareMenu: function(popup, sites) {
  241.     const ns = this.ns;
  242.     const sticky = popup.getAttribute("sticky") == "true";
  243.     
  244.     popup.removeAttribute("disabled");
  245.     
  246.     if (this._currentPopup && this._currentPopup != popup) {
  247.       this._currentPopup.hidePopup();
  248.     }
  249.     this._currentPopup = popup;
  250.     
  251.     
  252.     
  253.     var j, k, node;
  254.     
  255.     const global = ns.jsEnabled;
  256.     const blockUntrusted = global && ns.alwaysBlockUntrustedContent;
  257.     
  258.     var allSeps = popup.getElementsByTagName("menuseparator");
  259.    
  260.     var seps = { insert: null, stop: null, global: null, untrusted: null };
  261.     var sepName;
  262.     for (j = allSeps.length; j-- > 0;) {
  263.       sepName = (node = allSeps[j]).className;
  264.       node.hidden = false;
  265.       for (k in seps) {
  266.         if (sepName.indexOf("-" + k) > -1) {
  267.           seps[k] = node;
  268.         }
  269.       }
  270.     }
  271.     
  272.     delete allSeps;
  273.  
  274.     const miGlobal = seps.global.nextSibling;
  275.     
  276.     if (global || ns.getPref("showGlobal")) {
  277.       miGlobal.hidden = seps.global.hidden = false;
  278.       miGlobal.setAttribute("label", this.getString((global ? "forbid" : "allow") + "Global"));
  279.       miGlobal.setAttribute("oncommand", "noscriptOverlay.menuCmd(this)");
  280.       miGlobal.setAttribute("tooltiptext", this.statusIcon.getAttribute("tooltiptext"));
  281.       miGlobal.setAttribute("class", "menuitem-iconic noscript-glb " + (global ? "noscript-forbid" : "noscript-allow"));
  282.     } else {
  283.       miGlobal.hidden = seps.global.hidden = true;
  284.     }
  285.     
  286.     node = miGlobal.nextSibling;
  287.     const mainMenu = node.parentNode;
  288.     
  289.     
  290.     
  291.     
  292.     var tempMenuItem = $("noscript-revoke-temp-mi");
  293.     if (node != tempMenuItem) {
  294.       node = mainMenu.insertBefore(tempMenuItem, node);
  295.     }
  296.     
  297.     var tempSites = ns.gTempSites.sitesString;
  298.     tempSites = tempSites && (tempSites + " " + ns.tempSites.sitesString).replace(/\s+$/g, '') || ns.tempSites.sitesString;
  299.  
  300.     if ((tempSites || ns.objectWhitelistLen || ns.clearClickHandler && ns.clearClickHandler.whitelistLen) && ns.getPref("showRevokeTemp", true)) {
  301.       node.hidden = seps.global.hidden = false;
  302.       node.setAttribute("tooltiptext", this.revokeTooltip(tempSites));
  303.     } else {
  304.       node.hidden = true;
  305.     }
  306.     node = node.nextSibling;
  307.     
  308.     tempMenuItem = $("noscript-temp-allow-page-mi");
  309.     if (node != tempMenuItem) {
  310.       mainMenu.insertBefore(tempMenuItem, node)
  311.     } else {
  312.       node = node.nextSibling;
  313.     }
  314.     
  315.     var xssMenu = $("noscript-xss-menu");
  316.     
  317.     if (xssMenu && node != xssMenu) {
  318.       mainMenu.insertBefore(xssMenu, node);
  319.     }
  320.     this.populateXssMenu(xssMenu.firstChild);
  321.     this.syncXssWidget(xssMenu);
  322.     
  323.  
  324.     this.prepareOptItems(popup);
  325.       
  326.     var untrustedMenu = null,
  327.         recentMenu = null,
  328.         pluginsMenu = null;
  329.  
  330.     if (seps.untrusted) {
  331.       
  332.       pluginsMenu = $("noscript-menu-blocked-objects");
  333.       recentMenu = $("noscript-menu-recent-blocked");
  334.       untrustedMenu = $("noscript-menu-untrusted");
  335.       // cleanup untrustedCount display
  336.       untrustedMenu.setAttribute("label", untrustedMenu.getAttribute("label").replace(/ \(\d+\)$/, ""));
  337.       
  338.       with (seps.untrusted) {
  339.         if ((extraNode = nextSibling) != pluginsMenu) {
  340.           for each(node in [pluginsMenu, recentMenu, untrustedMenu]) {
  341.             parentNode.insertBefore(node, extraNode);
  342.           }
  343.         }
  344.       }
  345.       
  346.       extraNode = $("noscript-mi-recent-blocked-reset"); // save reset command
  347.       // descend from menus to popups and clear children
  348.       for each(node in [pluginsMenu = pluginsMenu.firstChild, recentMenu = recentMenu.firstChild, untrustedMenu = untrustedMenu.firstChild])
  349.         while(node.firstChild) node.removeChild(node.firstChild);
  350.       
  351.       recentMenu.appendChild(extraNode);
  352.     }
  353.     
  354.     node = seps.insert.nextSibling;
  355.     
  356.     var remNode;
  357.     while (node && (node != seps.stop)) {
  358.       remNode = node;
  359.       node = node.nextSibling;
  360.       if (remNode != untrustedMenu && remNode != xssMenu)
  361.         mainMenu.removeChild(remNode);
  362.     }
  363.  
  364.     mainMenu.appendCmd = function(n) { this.insertBefore(n, seps.stop); };
  365.  
  366.     sites = sites || this.getSites();
  367.     j = sites.indexOf(sites.topURL);
  368.     var topDown = !/-sep-stop\b/.test(mainMenu.lastChild.className); 
  369.     if (j > -1 && j != (topDown ? sites.length - 1 : 0)) {
  370.       sites.splice(j, 1);
  371.       if (topDown) sites.push(sites.topURL);
  372.       else sites.unshift(sites.topURL);
  373.     }
  374.     
  375.     
  376.     try {
  377.       this.populatePluginsMenu(mainMenu, pluginsMenu, sites.pluginExtras);
  378.     } catch(e) {
  379.       if(ns.consoleDump) ns.dump("Error populating plugins menu: " + e);
  380.     }
  381.     var site, enabled, isTop, lev;
  382.     var jsPSs = ns.jsPolicySites;
  383.     var matchingSite;
  384.     var menuGroups, menuSites, menuSite, scount;
  385.     var domain, pos, baseLen, dp;
  386.     var untrusted;
  387.     var cssClass;
  388.  
  389.     var domainDupChecker = {
  390.       domains: {},
  391.       check: function(d) {
  392.         return this.domains[d] || !(this.domains[d] = true);
  393.       }
  394.     };
  395.     
  396.     const locked = ns.locked;
  397.     const addressOnly = locked;
  398.     const showAddress = addressOnly || ns.getPref("showAddress", false);;
  399.     const showDomain = !addressOnly && ns.getPref("showDomain", false);
  400.     const showBase = !addressOnly && ns.getPref("showBaseDomain", true);
  401.     const showUntrusted = ns.getPref("showUntrusted", true);
  402.     const showDistrust = ns.getPref("showDistrust", true);
  403.     const showNothing = !(showAddress || showDomain || showBase || showUntrusted);
  404.     // const forbidImpliesUntrust = ns.forbidImpliesUntrust;
  405.     
  406.     const showPermanent = ns.getPref("showPermanent", true);
  407.     const showTemp = !locked && ns.getPref("showTemp", true);
  408.     
  409.     var parent = null, extraNode = null;
  410.     var untrustedCount = 0, unknownCount = 0, tempCount = 0;
  411.     const untrustedSites = ns.untrustedSites;
  412.     var docJSBlocked = false;
  413.     
  414.     menuGroups = [];
  415.     for (j = 0; j < sites.length; j++) {
  416.       site = sites[j];
  417.       
  418.       matchingSite = jsPSs.matches(site);
  419.       untrusted = untrustedSites.matches(site);
  420.       if (untrusted) {
  421.         matchingSite = null;
  422.       } else if (blockUntrusted && !matchingSite) {
  423.         matchingSite = site;
  424.       }
  425.       
  426.       isTop = site == sites.topURL;
  427.       enabled = !!matchingSite;
  428.       docJSBlocked = enabled && isTop && !this.currentBrowser.webNavigation.allowJavascript;
  429.       if (docJSBlocked) enabled = false;
  430.       
  431.       if (enabled && !global || (matchingSite = untrusted)) {
  432.         if (domainDupChecker.check(matchingSite)) continue;
  433.         menuSites = [matchingSite];
  434.       } else {
  435.         domain = !ns.isForbiddenByHttpsStatus(site) && ns.getDomain(site);
  436.         
  437.         if ((dp = ns.getPublicSuffix(domain)) == domain || // exclude TLDs
  438.             // ns.ignorePorts && /:\d+$/.test(site) &&
  439.             ns.isJSEnabled(domain) != enabled // exclude ancestors with different permissions
  440.           ) {
  441.           domain = null; 
  442.         }
  443.         
  444.         menuSites = (showAddress || showNothing || !domain) ? [site] : [];
  445.         if (domain && (showDomain || showBase)) {
  446.           baseLen = domain.length;
  447.           if (dp) 
  448.             baseLen -= (domain.lastIndexOf(".", baseLen - dp.length - 2) + 1); 
  449.           if (baseLen == domain.length) {
  450.             // IP or 2nd level domain
  451.             if (!domainDupChecker.check(domain)) {
  452.               menuSites.push(domain);
  453.             }
  454.           } else {
  455.             dp = domain;
  456.             for (pos = 0; (pos = domain.indexOf('.', pos)) > 0; dp = domain.substring(++pos)) {
  457.               if (baseLen == dp.length) {
  458.                 if (menuSites.length > 0 && !showBase) continue;
  459.               } else {
  460.                 if (!showDomain) continue;
  461.               }
  462.               if (!domainDupChecker.check(dp)) {
  463.                 menuSites.push(dp);
  464.               }
  465.               if (baseLen == dp.length) break;
  466.             }
  467.           }
  468.         }  
  469.       }
  470.       menuSites.isTop = isTop;
  471.       menuSites.enabled = enabled;
  472.       menuGroups.push(menuSites);
  473.     }
  474.     
  475.     
  476.     var blurred;
  477.  
  478.     const untrustedFrag = showUntrusted ? document.createDocumentFragment() : null;
  479.     const mainFrag = document.createDocumentFragment();
  480.     const sep = document.createElement("menuseparator");
  481.     
  482.     j = menuGroups.length;
  483.     
  484.     var refMI = document.createElement("menuitem");
  485.     refMI.setAttribute("oncommand", "noscriptOverlay.menuCmd(this)");
  486.     if (sticky && (this.liveReload || j > 1 || enabled)) {
  487.       refMI.setAttribute("closemenu", "none");
  488.     }
  489.     
  490.     recentMenu.parentNode.hidden = true;
  491.     if (recentMenu && ns.getPref("showRecentlyBlocked"))
  492.       (function() {
  493.         const level = ns.getPref("recentlyBlockedLevel") || ns.preferredSiteLevel;
  494.         const max = ns.getPref("recentlyBlockedCount");
  495.         var s, dejaVu = [], count = 0,
  496.             recent = ns.recentlyBlocked;
  497.         
  498.         for (var j = recent.length; j-- > 0;) {
  499.           
  500.           s = recent[j];
  501.           
  502.         
  503.           if (!s || sites.indexOf(s) > -1) continue;
  504.           
  505.           var jsEnabled = ns.isJSEnabled(s);
  506.           
  507.           if (jsEnabled && (!ns.contentBlocker || ns.isAllowedObject("!", "*", s)))
  508.             continue;
  509.           
  510.           s = ns.getQuickSite(s, level);
  511.           if (dejaVu.indexOf(s) > -1)
  512.             continue;
  513.           
  514.           dejaVu.push(s);
  515.           
  516.           if (!count) {
  517.             recentMenu.parentNode.hidden = false;
  518.           }
  519.           recentMenu.appendChild(sep.cloneNode(false));
  520.           
  521.           var node = refMI.cloneNode(false);
  522.           var cssClass = "noscript-cmd menuitem-iconic noscript-allow-from";
  523.           
  524.           node.setAttribute("tooltiptext", ns.getString("allowed.no"));
  525.           node.setAttribute("statustext", s);
  526.           if (locked || ns.isForbiddenByHttpsStatus(s)) node.setAttribute("disabled", "true");
  527.           
  528.           if (jsEnabled) {
  529.             cssClass += " noscript-plugin";
  530.           } else {
  531.             node.setAttribute("class", cssClass);
  532.             node.setAttribute("label", ns.getString("allowFrom", [s]));
  533.             recentMenu.appendChild(node);
  534.             node = node.cloneNode(false);
  535.           } 
  536.           
  537.           node.setAttribute("class", cssClass + " noscript-temp");
  538.           node.setAttribute("label", ns.getString("allowTempFrom", [s]));
  539.           recentMenu.appendChild(node);
  540.           
  541.           if (++count >= max) break;
  542.         }
  543.         
  544.       })();
  545.     
  546.     
  547.     
  548.     
  549.     
  550.     
  551.     if (j > 0 && seps.stop.previousSibling.nodeName != "menuseparator")
  552.       mainFrag.appendChild(sep.cloneNode(false));
  553.     
  554.     while (j-- > 0) {
  555.       
  556.       menuSites = menuGroups[j];
  557.       isTop = menuSites.isTop;
  558.       enabled = menuSites.enabled;
  559.       
  560.       if (untrustedFrag && untrustedFrag.firstChild) {
  561.         untrustedFrag.appendChild(sep.cloneNode(false));
  562.       }
  563.       
  564.       scount = menuSites.length;
  565.       if (scount > 0 && mainFrag.lastChild && mainFrag.lastChild.tagName != "menuseparator")
  566.         mainFrag.appendChild(sep.cloneNode(false));
  567.         
  568.       while (scount-- > 0) {
  569.         menuSite = menuSites[scount];
  570.         
  571.         untrusted = !enabled && (blockUntrusted || ns.isUntrusted(menuSite));
  572.         if (untrusted) 
  573.           untrustedCount++;
  574.         else if (!enabled)
  575.           unknownCount++;
  576.         
  577.         parent = (untrusted && showUntrusted) ? untrustedFrag : mainFrag;
  578.         if (!parent) continue;
  579.                 
  580.         domain = isTop && docJSBlocked ? "[ " + menuSite + " ]" : menuSite;
  581.         
  582.         node = refMI.cloneNode(false);
  583.         if (isTop) {
  584.           cssClass = "noscript-toplevel noscript-cmd";
  585.           // can we make it default here?
  586.         }
  587.         else cssClass = "noscript-cmd";
  588.         
  589.         
  590.         blurred = false;
  591.         if (locked || (enabled ? ns.isMandatory(menuSite) : blurred = ns.isForbiddenByHttpsStatus(menuSite))) {
  592.           node.setAttribute("disabled", "true");
  593.         } else {
  594.           cssClass += " menuitem-iconic ";
  595.           if (enabled && ns.isTemp(menuSite)) {
  596.             cssClass += " noscript-temp";
  597.             tempCount++;
  598.           }
  599.         }
  600.         
  601.         node.setAttribute("label", this.getString((enabled ? "forbidLocal" : "allowLocal"), [domain]));
  602.         node.setAttribute("statustext", menuSite);
  603.         node.setAttribute("tooltiptext",
  604.           this.getString("allowed." + (enabled ? "yes" : "no")));
  605.  
  606.         
  607.         node.setAttribute("class", cssClass + (enabled ? " noscript-forbid" : " noscript-allow"));
  608.         
  609.         if ((showPermanent || enabled) && !(global && enabled)) 
  610.           parent.appendChild(node);
  611.         
  612.         if (!locked) {
  613.           if (showTemp && !(enabled || blurred)) {
  614.             extraNode = node.cloneNode(false);
  615.             extraNode.setAttribute("label", this.getString("allowTemp", [domain]));
  616.             extraNode.setAttribute("class", cssClass + " noscript-temp noscript-allow");
  617.             parent.appendChild(extraNode);
  618.           }
  619.           if (((showUntrusted && untrustedMenu || showDistrust) && !(domain in jsPSs.sitesMap) || blockUntrusted) && !untrusted) {
  620.             parent = (showUntrusted && !blockUntrusted ? untrustedFrag : mainFrag);
  621.             extraNode = refMI.cloneNode(false);
  622.             extraNode.setAttribute("label", this.getString("distrust", [menuSite]));
  623.             extraNode.setAttribute("statustext", menuSite);
  624.             extraNode.setAttribute("class", cssClass + " noscript-distrust");
  625.             extraNode.setAttribute("tooltiptext", node.getAttribute("tooltiptext"));
  626.             parent.appendChild(extraNode);
  627.           }
  628.         }
  629.       }
  630.       
  631.       
  632.     }
  633.     if (untrustedFrag && untrustedFrag.firstChild) {
  634.       if (untrustedCount > 0) 
  635.         with (untrustedMenu.parentNode)
  636.           setAttribute("label", getAttribute("label") +
  637.             " (" + untrustedCount + ")"); // see above for cleanup
  638.           
  639.       untrustedMenu.appendChild(untrustedFrag);
  640.     }
  641.  
  642.     mainMenu.appendCmd(mainFrag);
  643.     
  644.     // temp allow all this page
  645.     if (!(tempMenuItem.hidden = !(unknownCount && ns.getPref("showTempAllowPage", true)))) {
  646.       tempMenuItem.setAttribute("tooltiptext", this.allowPage(false, true).join(", "));
  647.     }
  648.  
  649.     
  650.     // allow all this page
  651.     node = $("noscript-allow-page-mi");
  652.     if (node.nextSibling != tempMenuItem) {
  653.       tempMenuItem.parentNode.insertBefore(node, tempMenuItem);
  654.     }
  655.     if (!(node.hidden = unknownCount == 0 || !ns.getPref("showAllowPage", true))) {
  656.       node.setAttribute("tooltiptext", this.allowPage(true, true).join(", "));
  657.     }
  658.     
  659.     // make permanent
  660.     node = $("noscript-temp2perm-mi");
  661.     if (tempMenuItem.nextSibling != node) {
  662.       tempMenuItem.parentNode.insertBefore(node, tempMenuItem.nextSibling);
  663.     }
  664.     if (!(node.hidden = tempCount == 0 || !ns.getPref("showTempToPerm"))) {
  665.       node.setAttribute("tooltiptext", this.tempToPerm(true).join(", "));
  666.     }
  667.     
  668.     
  669.     
  670.     this.normalizeMenu(untrustedMenu, true);
  671.     this.normalizeMenu(mainMenu, false);
  672.  
  673.   },
  674.  
  675.   reverse: function(m) {
  676.     var a = [];
  677.     var mi;
  678.     while((mi = m.lastChild)) {
  679.       a.push(m.removeChild(mi));
  680.     }
  681.     for each(mi in a) {
  682.       m.appendChild(mi);
  683.     }
  684.   },
  685.   
  686.  
  687.   
  688.   populatePluginsMenu: function(mainMenu, menu, extras) {
  689.     if (!menu) return;
  690.     
  691.     menu.parentNode.hidden = true;
  692.     const ns = this.ns;
  693.     if (!(extras && ns.getPref("showBlockedObjects", true)))
  694.       return;
  695.     
  696.     var egroup, e, node, j;
  697.     var pluginExtras = [];
  698.     var i = 0;
  699.     for each(egroup in extras) {
  700.       for (j = egroup.length; j-- > 0;) {
  701.         e = egroup[j];
  702.          
  703.         if (ns.isAllowedObject(e.url, e.mime))
  704.           continue;
  705.          
  706.         if(typeof(e) != "object" || (e.tag && !e.placeholder)) continue;
  707.         node = document.createElement("menuitem");
  708.         
  709.         e.label = e.label || ns.mimeEssentials(e.mime) + "@" + ns.urlEssentials(e.url);
  710.         e.title = e.title || e.label.split("@")[0] + "@" + e.url;
  711.  
  712.         node.setAttribute("label", this.getString("allowTemp", [e.label]));
  713.         node.setAttribute("tooltiptext", e.title);
  714.         node.setAttribute("oncommand", "noscriptOverlay.allowObject(" + i + ")");
  715.         node.setAttribute("class", "menuitem-iconic noscript-cmd noscript-temp noscript-allow");
  716.         node.style.listStyleImage = ns.cssMimeIcon(e.mime, 16);
  717.         menu.appendChild(node);
  718.         pluginExtras[i++] = e;
  719.       }
  720.     }
  721.     if (pluginExtras.length) {
  722.       noscriptOverlay.menuPluginExtras = pluginExtras;
  723.       mainMenu.addEventListener("popuphidden", function(ev) {
  724.           if (ev.currentTarget != ev.target) return;
  725.           ev.currentTarget.removeEventListener(ev.type, arguments.callee, false);
  726.           noscriptOverlay.menuPluginExtras = null;
  727.           noscriptOverlay.menuPluginSites = null;
  728.       }, false);
  729.       var pluginSites = {};
  730.       i = 0;
  731.       for each(e in pluginExtras) {
  732.         if(!(e.site && e.mime) || ns.isAllowedObject(e.site, e.mime, e.site))
  733.           continue;
  734.         
  735.         if (e.site in pluginSites) {
  736.           if (pluginSites[e.site].indexOf(e.mime) > -1) 
  737.             continue;
  738.           pluginSites[e.site].push(e.mime);
  739.         } else {
  740.           pluginSites[e.site] = ["*", e.mime];
  741.         }
  742.         i++;
  743.       }
  744.       if (i) {
  745.         noscriptOverlay.menuPluginSites = [];
  746.         i = 0;
  747.         var mime;
  748.         for (var site in pluginSites) {
  749.           menu.appendChild(document.createElement("menuseparator"));
  750.           for each(mime in pluginSites[site]) {
  751.             node = document.createElement("menuitem");
  752.             node.setAttribute("label", this.getString("allowTemp", [ns.mimeEssentials(mime) + "@" + site]));
  753.             node.setAttribute("tooltiptext", mime + "@" + site);
  754.             node.setAttribute("oncommand", "noscriptOverlay.allowObjectSite(" + i + ")");
  755.             node.setAttribute("class", "menuitem-iconic noscript-temp noscript-cmd noscript-allow");
  756.             if(mime != "*")
  757.               node.style.listStyleImage = node.style.listStyleImage = ns.cssMimeIcon(mime, 16);
  758.  
  759.             menu.appendChild(node);
  760.             noscriptOverlay.menuPluginSites[i++] = [site, mime];
  761.           }
  762.         }
  763.       }
  764.       menu.parentNode.hidden = false;
  765.     }
  766.   },
  767.   
  768.   allowPage: function(permanent, justTell) {
  769.     const ns = this.ns;
  770.     const sites = this.getSites();
  771.     const unknown = [];
  772.     const level = ns.getPref("allowPageLevel", 0) || ns.preferredSiteLevel;
  773.     const trusted = ns.jsPolicySites;
  774.     const tempToPerm = permanent === -1;
  775.     var site;
  776.     for (var j = sites.length; j-- > 0;) {
  777.       if (tempToPerm) {
  778.         site = trusted.matches(sites[j]);
  779.         if (!(site && ns.isTemp(site)) || ns.isUntrusted(site)) continue;
  780.       } else {
  781.         site = ns.getQuickSite(sites[j], level);
  782.         if (ns.isJSEnabled(site) || ns.isUntrusted(site)) continue;
  783.       }
  784.       unknown.push(site);
  785.     }
  786.     if (!justTell) {
  787.       if (unknown.length) {
  788.         var browser = this.currentBrowser;
  789.         ns.setExpando(browser, "allowPageURL", content.document.URL);
  790.         this.safeAllow(unknown, true, !permanent);
  791.       }
  792.     }
  793.     return unknown;
  794.   },
  795.   
  796.   tempToPerm: function(justTell) {
  797.     return this.allowPage(-1, justTell);
  798.   },
  799.   
  800.   allowObject: function(i) {
  801.     if(this.menuPluginExtras && this.menuPluginExtras[i]) {
  802.       var e = this.menuPluginExtras[i];
  803.       if(e.placeholder) {
  804.         this.ns.checkAndEnablePlaceholder(e.placeholder);
  805.       } else if (this.ns.confirmEnableObject(window, e)) {
  806.         this.allowObjectURL(e.url, e.mime);
  807.       }
  808.     }
  809.   },
  810.   
  811.   allowObjectSite: function(i) {
  812.     if(this.menuPluginSites && this.menuPluginSites[i]) {
  813.       this.allowObjectURL(this.menuPluginSites[i][0], this.menuPluginSites[i][1]);
  814.     }
  815.   },
  816.   allowObjectURL: function(url, mime) {
  817.     this.ns.allowObject(url, mime);
  818.     this.ns.reloadAllowedObjects(this.currentBrowser);
  819.   },
  820.   
  821.   normalizeMenu: function(menu, hideParentIfEmpty) {
  822.     if (!menu) return;
  823.     var prev = null;
  824.     var wasSep = true;
  825.     var isSep, haveMenu = false;
  826.     for (var i = menu.firstChild; i; i = i.nextSibling) {
  827.       if (!i.hidden) {
  828.         isSep = i.nodeName == "menuseparator";
  829.         if (isSep && wasSep) {
  830.           i.hidden = true;
  831.         } else {
  832.           haveMenu = haveMenu || !isSep;
  833.           prev = i;
  834.           wasSep = isSep;
  835.         }
  836.       }
  837.     }
  838.     
  839.     if (prev && wasSep) {
  840.       prev.hidden = true;
  841.     }
  842.     if (hideParentIfEmpty && menu.parentNode) {
  843.       menu.parentNode.hidden = !haveMenu;
  844.     }
  845.   }
  846. ,
  847.   getBrowserDoc: function(browser) {
  848.     return browser && browser.contentWindow && browser.contentWindow.document || null;
  849.   }
  850. ,
  851.   revokeTemp: function() {
  852.     this.ns.safeCapsOp(function(ns) {
  853.       ns.eraseTemp();
  854.       noscriptOverlay.syncUI();
  855.     }, this.ns.getPref("autoReload.allTabsOnPageAction", true) ? this.ns.RELOAD_ALL : this.ns.RELOAD_CURRENT);
  856.   }
  857. ,
  858.   menuCmd: function(menuItem) {
  859.     var site = null;
  860.     var reloadPolicy = 0;
  861.     var cl = menuItem.getAttribute("class") || "";
  862.     var cmd = cl.match(/-(forbid|allow|distrust)\b/);
  863.     if (!(cmd && (cmd = cmd[1]))) return;
  864.     var enabled = cmd == "allow";
  865.     var temp = /-temp\b/.test(cl);
  866.     if (/-glb\b/.test(cl)) {
  867.       // global allow/forbid
  868.       if (enabled && this.ns.getPref("globalwarning", true) &&
  869.           !this.prompter.confirm(window, this.getString("global.warning.title"),
  870.                                 this.getString("global.warning.text"))
  871.         ) return;
  872.     } else {
  873.       // local allow/forbid
  874.       site = menuItem.getAttribute("statustext");
  875.       if (!site) return;
  876.       
  877.       if (cmd == "distrust") {
  878.         this.ns.setUntrusted(site, true);
  879.       }
  880.       
  881.       if (menuItem.getAttribute("closemenu") == "none") {
  882.         // sticky UI feedback
  883.         if (this._currentPopup) {
  884.           this._currentPopup.setAttribute("disabled", "true");
  885.         }
  886.         this._reloadDirty = true;
  887.         reloadPolicy = this.liveReload ? this.ns.RELOAD_CURRENT : this.ns.RELOAD_NO;
  888.       }
  889.       
  890.       if (enabled && /\ballow-from\b/.test(cl)) {
  891.         reloadPolicy = this.ns.RELOAD_NONE;
  892.         this.ns.allowObject(site, "*");
  893.         if (this.ns.isJSEnabled(site)) return;
  894.       }
  895.       
  896.     }
  897.     this.safeAllow(site, enabled, temp, reloadPolicy);
  898.   }
  899. ,
  900.   safeAllow: function(site, enabled, temp, reloadPolicy) {
  901.     const ns = this.ns;
  902.     var webNav = this.currentBrowser.webNavigation;
  903.     
  904.     if (!reloadPolicy && (site instanceof Array) &&
  905.           !ns.getPref("autoReload.allTabsOnPageAction", true)) {
  906.       reloadPolicy = 1 // current tab only, for multiple items
  907.     }
  908.     var allowTemp = enabled && temp;
  909.     
  910.     function op(ns) {
  911.       if (site) {    
  912.         ns.setTemp(site, allowTemp);
  913.         ns.setJSEnabled(site, enabled, false, ns.mustCascadeTrust(site, temp));
  914.         
  915.         if (enabled && !webNav.allowJavascript) {
  916.           var curSite = ns.getSite(webNav.currentURI.spec);
  917.           if (ns.isJSEnabled(curSite)) {
  918.             // force reload
  919.             if (ns.jsEnabled) {
  920.               ns._lastSnapshot.add(curSite); 
  921.             } else {
  922.               ns._lastSnapshot.remove(curSite); 
  923.             }
  924.           }
  925.           webNav.allowJavascript = true;
  926.         }
  927.       } else {
  928.         ns.jsEnabled = enabled;
  929.       }
  930.       if (reloadPolicy == ns.RELOAD_NO) {
  931.         noscriptOverlay._syncUINow();
  932.         if (!allowTemp) ns.savePrefs();
  933.       }
  934.       else noscriptOverlay.syncUI();
  935.     }
  936.     
  937.     if (reloadPolicy == ns.RELOAD_NO) {
  938.       op(ns);
  939.     } else {
  940.       ns.setExpando(window.content, "contentLoaded", false);
  941.       ns.safeCapsOp(op, reloadPolicy, allowTemp);
  942.     }
  943.   }
  944. ,
  945.   
  946.   get statusIcon() {
  947.     var statusIcon = $("noscript-statusIcon") || $("noscript-tbb");
  948.     if (!statusIcon) return null; // avoid mess with early calls
  949.     delete this.statusIcon;
  950.     return this.statusIcon = statusIcon;
  951.   },
  952.  
  953.   getIcon: function(node) {
  954.     if (typeof(node) != "object") node = $(node);
  955.     return node.ownerDocument.defaultView.getComputedStyle(node, null)
  956.             .listStyleImage.replace(/.*url\s*\(\s*"?([^"\s\)]*).*/g, '$1');
  957.   },
  958.   
  959.   getStatusClass: function(lev, inactive, currentClass) {
  960.     return "noscript-" + (inactive ? "inactive-" : "") + lev;
  961.   },
  962.   updateStatusClass: function(node, className) {
  963.     if (!className) className = this.statusIcon.className.replace(/.*(\bnoscript-\S*(?:yes|no|glb|prt|yu|untrusted)).*/, "$1");
  964.     node.className = (node.className.replace(/\bnoscript-\S*(?:yes|no|glb|prt|yu|untrusted)\b/g, "") + " " + className).replace(/\s{2,}/g, " ");
  965.   }
  966. ,
  967.   _syncTimeout: null,
  968.   syncUI: function(w) {
  969.     if (w) {
  970.       if (w != window.content) return;
  971.     } else {
  972.       w = window.content;
  973.     }
  974.     
  975.     if (this._syncTimeout) {
  976.       window.clearTimeout(this._syncTimeout);
  977.     }
  978.     this._syncTimeout = window.setTimeout(function() {
  979.       if (w != window.content) return;
  980.       noscriptOverlay._syncUINow();
  981.     }, 400);
  982.   },
  983.   
  984.   syncXssWidget: function(widget) {
  985.     if (!widget) widget = $("noscript-statusXss");
  986.     if (!widget) return;
  987.     const ns = this.ns;
  988.     var unsafeRequest = ns.requestWatchdog.getUnsafeRequest(this.currentBrowser);
  989.     if (unsafeRequest && !unsafeRequest.issued) {
  990.       widget.removeAttribute("hidden");
  991.       widget.setAttribute("tooltiptext", "XSS [" +
  992.                   ns.getSite(unsafeRequest.origin) + "]->[" + 
  993.                   ns.getSite(unsafeRequest.URI.spec) + "]");
  994.     } else {
  995.       widget.setAttribute("hidden", "true");
  996.     }
  997.   },
  998.   
  999.   syncRedirectWidget: function() {
  1000.     var widget = $("noscript-statusRedirect");
  1001.     if (!widget) return;
  1002.     var info = this.getMetaRefreshInfo();
  1003.     if (!info) {
  1004.       widget.setAttribute("hidden", true);
  1005.       return;
  1006.     }
  1007.     widget.removeAttribute("hidden");
  1008.     widget.setAttribute("tooltiptext",
  1009.         this.getString("metaRefresh.notify.follow") + " [" + info.uri + "]"); 
  1010.   },
  1011.   
  1012.   get stickyUI() {
  1013.     var ui = $("noscript-sticky-ui");
  1014.     if (ui == null) return null;
  1015.     delete this.stickyUI;
  1016.     if (!ui.openPopup) {
  1017.       ui = null;
  1018.     } else {
  1019.       ui.hidden = false;
  1020.       
  1021.       if ("Browser" in window) {
  1022.         // Fennec tweaks
  1023.         ui.className = "noscript-menu";
  1024.     
  1025.         var p = document.getAnonymousElementByAttribute(ui, "class", "popup-internal-box");
  1026.         var d = p.ownerDocument;
  1027.         ["scrollbutton-up", "scrollbutton-down"].forEach(function(id) {
  1028.           var s = d.getAnonymousElementByAttribute(p, "anonid", id).style;
  1029.           s.minHeight = "40px";
  1030.           s.borderColor = "#888";
  1031.           s.borderStyle = "solid";
  1032.           s.borderWidth = "1px";
  1033.         });
  1034.       }
  1035.     }
  1036.     return this.stickyUI = ui;
  1037.   },
  1038.   
  1039.   get useStickyUI() {
  1040.     return this.ns.getPref("stickyUI");
  1041.   },
  1042.   
  1043.     
  1044.   showUI: function() {
  1045.     var popup = null;
  1046.     
  1047.     var useSticky = this.stickyUI && this.ns.getPref("stickyUI.onKeyboard");
  1048.  
  1049.     popup =  (useSticky && (popup = this.stickyUI)) ||
  1050.       $("noscript-status-popup");
  1051.     if (!this.isOpenOrJustClosed(popup)) {
  1052.       popup._context = !useSticky;
  1053.       popup.showPopup(null, -1, -1, "context", null, null);
  1054.     }
  1055.   }
  1056.   
  1057. ,
  1058.   get notificationPos() {
  1059.     return this.notifyBottom ? "bottom" : "top";
  1060.   },
  1061.   get altNotificationPos() {
  1062.     return this.notificationPos == "top" ? "bottom" : "top";
  1063.   }
  1064.   getNotificationBox: function(pos, browser) {
  1065.     var gb = getBrowser();
  1066.     browser = browser || gb.selectedBrowser;
  1067.     if (!pos) pos = this.notificationPos;
  1068.     
  1069.     if (gb.getMessageForBrowser) return gb.getMessageForBrowser(browser, pos); // Fx <= 1.5 
  1070.     if (!gb.getNotificationBox) return null; // SeaMonkey
  1071.  
  1072.     var nb = gb.getNotificationBox(browser);
  1073.     
  1074.     if (nb) 
  1075.       this.patchNotificationBox(nb, pos);
  1076.    
  1077.     return nb;
  1078.   },
  1079.   patchNotificationBox: function(nb, pos) {
  1080.     if (nb._noscriptPatched) return;
  1081.     
  1082.     nb._noscriptPatched = true;
  1083.     
  1084.     nb.__defineGetter__("_closedNotification", function() {
  1085.       var cn = this.__ns__closedNotification;
  1086.       return (cn && cn.parentNode ? cn : null);
  1087.     });
  1088.     
  1089.     nb.__defineSetter__("_closedNotification", function(cn) {
  1090.       this.__ns__closedNotification = cn;
  1091.     });
  1092.     
  1093.     if (pos != "bottom") return;
  1094.     
  1095.     nb._dom_ = {};
  1096.     const METHODS = this.notificationBoxPatch;
  1097.     for (m in METHODS) {
  1098.       nb._dom_[m] = nb[m];
  1099.       nb[m] = METHODS[m];
  1100.     }
  1101.  
  1102.     var stacks =  nb.getElementsByTagName("stack");
  1103.     var stack = null;
  1104.     for (var j = stacks.length; j-- > 0;) {
  1105.       if (stacks[j].getAttribute("class") == "noscript-bottom-notify") {
  1106.         stack = stacks[j];
  1107.         break;
  1108.       }
  1109.     }
  1110.     if (!stack) {
  1111.      stack = nb.ownerDocument.createElement("stack");
  1112.      stack.setAttribute("class", "noscript-bottom-notify");
  1113.      nb.appendChild(stack);
  1114.     }
  1115.     nb._noscriptBottomStack_ = stack;
  1116.   },
  1117.   
  1118.   notificationBoxPatch: {
  1119.     insertBefore: function(n, ref) {
  1120.       if (n.localName == "notification" && 
  1121.           n.getAttribute("value") == "noscript"
  1122.           && noscriptOverlay.notificationPos == "bottom") {
  1123.         const stack = this._noscriptBottomStack_;
  1124.         /*
  1125.          while (stack.firstChild) 
  1126.           stack.removeChild(stack.firstChild);
  1127.         
  1128.         stack.appendChild(n);
  1129.         */
  1130.         stack.insertBefore(n, null);
  1131.         var hbox = n.ownerDocument.getAnonymousElementByAttribute(
  1132.                       n, "class", "notification-inner outset");
  1133.         if (hbox) {
  1134.           var style = hbox.ownerDocument.defaultView.getComputedStyle(hbox, null);
  1135.           var borderProps = ['color', 'style', 'width'];
  1136.           var cssProp, jsProp, tmpVal;
  1137.           for (var p = borderProps.length; p-- > 0;) {
  1138.             cssProp = borderProps[p];
  1139.             jsProp = cssProp[0].toUpperCase() + cssProp.substring(1);
  1140.             tmpVal = style.getPropertyValue("border-bottom-" + cssProp);
  1141.             hbox.style["borderBottom" + jsProp] = style.getPropertyValue("border-top-" + cssProp);
  1142.             hbox.style["borderTop" + jsProp] = tmpVal;
  1143.           }
  1144.         }
  1145.         return n;
  1146.       }
  1147.       if(ref && ref.parentNode != this) {
  1148.         var priority = ref.priority;
  1149.         ref = null; 
  1150.         var notifications = this.allNotifications;
  1151.         for (var j = notifications.length; j-- > 0;) {
  1152.           if ((ref = notifications[j]).priority < priority && ref.parentNode == this)
  1153.             break;
  1154.         }
  1155.         if(j < 0) ref = null;
  1156.       }
  1157.       return this._dom_.insertBefore.apply(this, [n, ref]);
  1158.     },
  1159.     removeChild: function(n) {
  1160.       return (n.parentNode == this) ? this._dom_.removeChild.apply(this, arguments) : n.parentNode.removeChild(n); 
  1161.     }
  1162.   },
  1163.   
  1164.   getNsNotification: function(widget) {
  1165.     if (widget == null) return null;
  1166.     if (widget.localName == "notificationbox") return widget.getNotificationWithValue("noscript");
  1167.     return this.isNsNotification(widget) && widget || null;
  1168.   },
  1169.   isNsNotification: function(widget) {
  1170.     return widget && widget.getAttribute("value") == "noscript" || widget.popup == "noscript-notify-popup";
  1171.   },
  1172.   
  1173.   
  1174.   notificationShow: function(label, icon, canAppend) {
  1175.     // if (this.ns.consoleDump) this.ns.dump("Notification show " + Components.stack.caller + "," + (browser || this.currentBrowser).currentURI.spec);
  1176.     var box = this.getNotificationBox();
  1177.     if (box == null) return false;
  1178.     var pos = this.notificationPos;
  1179.     
  1180.     const gb = getBrowser();
  1181.     const browser = gb.selectedBrowser;
  1182.     
  1183.     const popup = "noscript-notify-popup";
  1184.     
  1185.     var widget = this.getNsNotification(box);
  1186.     if (widget) {
  1187.      if (widget.localName == "notification") {
  1188.        widget.label = label;
  1189.        widget.image = icon;
  1190.      } else {
  1191.        widget.text = label;
  1192.        widget.image = icon;
  1193.        if (canAppend) widget.removeAttribute("hidden");
  1194.      }
  1195.     
  1196.     } else {
  1197.      
  1198.       if (!canAppend) return false;
  1199.      
  1200.       var buttonLabel, buttonAccesskey;
  1201.       if (gb.getNotificationBox || /\baButtonAccesskey\b/i.test(gb.showMessage.toSource())) {
  1202.         const refWidget = $("noscript-options-menuitem");
  1203.         buttonLabel = refWidget.getAttribute("label");
  1204.         buttonAccesskey = refWidget.getAttribute("accesskey");
  1205.       } else { // Fx < 1.5
  1206.         buttonLabel = "";
  1207.         buttonAccesskey = "";
  1208.       }
  1209.       
  1210.       if (box.appendNotification) { // >= Fx 2.0
  1211.         // if (box._closedNotification && !box._closedNotification.parentNode) box._closedNotification = null; // work around for notification bug
  1212.         widget =  box.appendNotification(label, "noscript", icon, box.PRIORITY_WARNING_MEDIUM,
  1213.                   [ {label: buttonLabel, accessKey: buttonAccesskey,  popup: popup } ]);
  1214.         
  1215.       } else if (gb.showMessage) { // Fx <= 1.5.x
  1216.         gb.showMessage(browser, icon, label, 
  1217.               buttonLabel, null,
  1218.               null, popup, pos, true,
  1219.               buttonAccesskey);
  1220.         widget = this.getNsNotification(box);
  1221.       }
  1222.      
  1223.     }
  1224.     if (!widget) return false;
  1225.     
  1226.     const delay = this.notifyHide && this.notifyHideDelay || 0;
  1227.     if (delay) {
  1228.      if (this.notifyHideTimeout) window.clearTimeout(this.notifyHideTimeout);
  1229.      this.notifyHideTimeout = window.setTimeout(
  1230.        function() {
  1231.          if (browser == gb.selectedBrowser) {
  1232.             if ($(popup) == noscriptOverlay._currentPopup) {
  1233.               noscriptOverlay.notifyHideTimeout = window.setTimeout(arguments.callee, 1000);
  1234.               return;
  1235.             }
  1236.             noscriptOverlay.notificationHide(browser);
  1237.          }
  1238.        },
  1239.        1000 * delay);
  1240.     }
  1241.     return true;
  1242.   },
  1243.   
  1244.   getAltNotificationBox: function(browser, value, canAppend) {
  1245.     
  1246.     const box = (("Browser" in window) && Browser.getNotificationBox)
  1247.       ? Browser.getNotificationBox()
  1248.       : this.getNotificationBox(this.altNotificationPos, browser);
  1249.     if (canAppend || (box && 
  1250.         box.getNotificationWithValue &&
  1251.         box.getNotificationWithValue(value))) return null;
  1252.     return box;
  1253.   },
  1254.   
  1255.   notifyXSSOnLoad: function(requestInfo) {
  1256.     requestInfo.browser.addEventListener("DOMContentLoaded", function(ev) {
  1257.       requestInfo.browser.removeEventListener(ev.type, arguments.callee, false);
  1258.       if (requestInfo.unsafeRequest && requestInfo.unsafeRequest.issued) return;
  1259.       noscriptOverlay.notifyXSS(requestInfo);
  1260.     }, false);
  1261.   },
  1262.   
  1263.   notifyXSS: function(requestInfo) {
  1264.     const notificationValue = "noscript-xss-notification"; 
  1265.     const box = this.getAltNotificationBox(requestInfo.browser, notificationValue);
  1266.     if (!box) return;
  1267.  
  1268.     var origin = this.ns.getSite(requestInfo.unsafeRequest.origin);
  1269.     origin = (origin && "[" + origin + "]") || this.getString("untrustedOrigin");
  1270.     var label = this.getString("xss.notify.generic", [origin]);
  1271.     var icon = this.getIcon("noscript-statusXss");
  1272.     
  1273.     const refWidget = $("noscript-options-menuitem");
  1274.     var buttonLabel = refWidget.getAttribute("label");
  1275.     var buttonAccesskey = refWidget.getAttribute("accesskey");
  1276.     var popup = $("noscript-xss-popup");
  1277.     if ("Browser" in window) popup.className = "noscript-menu";
  1278.     
  1279.     const tabBrowser = getBrowser();
  1280.     if (tabBrowser.showMessage) { // Fx 1.5
  1281.       tabBrowser.showMessage(
  1282.           requestInfo.browser, 
  1283.           icon, label, 
  1284.           buttonLabel, null,
  1285.           null, popup.id, this.altNotificationPos, true,
  1286.           buttonAccesskey);
  1287.     } else { // Fx >= 2.0
  1288.       box.appendNotification(
  1289.         label, 
  1290.         notificationValue, 
  1291.         icon, 
  1292.         box.PRIORITY_WARNING_HIGH,
  1293.         [{
  1294.           label: buttonLabel,
  1295.           accessKey: buttonAccesskey,
  1296.           popup: popup.id
  1297.          }]
  1298.         );
  1299.     }
  1300.   },
  1301.   
  1302.   notifyMetaRefreshCallback: function(info) {
  1303.     noscriptOverlay.notifyMetaRefresh(info);
  1304.   },
  1305.   notifyMetaRefresh: function(info) {
  1306.     var browser = this.ns.dom.findBrowser(window, info.document.defaultView);
  1307.     if (!browser) return;
  1308.     
  1309.     const notificationValue = "noscript-metaRefresh-notification";
  1310.     const box = this.getAltNotificationBox(browser, notificationValue);
  1311.     var notification = null;
  1312.     
  1313.     if (box && this.ns.getPref("forbidMetaRefresh.notify", true)) {
  1314.       var label = this.getString("metaRefresh.notify", [info.uri, info.timeout])
  1315.       var icon = this.getIcon("noscript-statusRedirect");
  1316.         
  1317.       if (box.appendNotification) { // Fx 2
  1318.       
  1319.         notification = box.appendNotification(
  1320.           label, 
  1321.           notificationValue, 
  1322.           icon, 
  1323.           box.PRIORITY_INFO_HIGH,
  1324.           [{
  1325.               label: this.getString("metaRefresh.notify.follow"),
  1326.               accessKey: this.getString("metaRefresh.notify.follow.accessKey"),
  1327.               callback: function(notification, buttonInfo) {
  1328.                 noscriptOverlay.ns.doFollowMetaRefresh(info);
  1329.               }
  1330.            }]
  1331.           );
  1332.       }
  1333.       browser.addEventListener("beforeunload", function(ev) {
  1334.         if (ev.originalTarget == info.document || ev.originalTarget == browser) {
  1335.           browser.removeEventListener(ev.type, arguments.callee, false);
  1336.           if (notification && notification == box.currentNotification) {
  1337.             box.removeCurrentNotification();
  1338.           } else {
  1339.             noscriptOverlay.ns.doBlockMetaRefresh(info);
  1340.           }
  1341.           info = browser = null;
  1342.         }
  1343.       }, false);
  1344.     }
  1345.     
  1346.     this.setMetaRefreshInfo(info, browser);
  1347.   },
  1348.   
  1349.   setMetaRefreshInfo: function(value, browser) {
  1350.     return this.ns.setExpando(browser || this.currentBrowser, "metaRefreshInfo", value);
  1351.   },
  1352.   getMetaRefreshInfo: function(browser) {
  1353.     return this.ns.getExpando(browser || this.currentBrowser, "metaRefreshInfo");
  1354.   },
  1355.   followMetaRefresh: function(event) {
  1356.     this.ns.doFollowMetaRefresh(this.getMetaRefreshInfo(), event.shiftKey);
  1357.   },
  1358.   
  1359.   notifyJarDocument: function(info) {
  1360.     var browser = this.ns.dom.findBrowser(window, info.document.defaultView.top);
  1361.     if (!browser) return false;
  1362.     
  1363.     const notificationValue = "noscript-jarDoc-notification";
  1364.     const box = this.getAltNotificationBox(browser, notificationValue);
  1365.   
  1366.     if (!(box && box.appendNotification)) return false;
  1367.     
  1368.     var notification = null;
  1369.     
  1370.     var label = this.getString("jarDoc.notify", [info.uri]);
  1371.     var icon = this.getIcon("noscript-jar-opts");
  1372.  
  1373.     notification = box.appendNotification(
  1374.       label, 
  1375.       notificationValue, 
  1376.       icon, 
  1377.       box.PRIORITY_WARNING_HIGH,
  1378.       [{
  1379.           label: this.getString("notify.options"),
  1380.           accessKey: this.getString("notify.accessKey"),
  1381.           callback: function(notification, buttonInfo) {
  1382.             noscriptUtil.openJarOptions();
  1383.           }
  1384.        }]
  1385.       );
  1386.     browser.addEventListener("beforeunload", function(ev) {
  1387.       if (ev.originalTarget == info.document || ev.originalTarget == browser) {
  1388.         browser.removeEventListener(ev.type, arguments.callee, false);
  1389.         if (notification && notification == box.currentNotification) {
  1390.           box.removeCurrentNotification();
  1391.         } 
  1392.         info = browser = notification = null;
  1393.       }
  1394.     }, false);
  1395.     
  1396.     return true;
  1397.   },
  1398.   
  1399.   get supportsNotifications() {
  1400.     delete this.supportsNotification;
  1401.     return this.supportsNotification = !!document.getElementsByTagName("notificationbox").length;
  1402.   },
  1403.   
  1404.   notifyABE: function(info) {
  1405.     var browser = info.browser;
  1406.     
  1407.     const notificationValue = "noscript-abe-notification";
  1408.     const box = this.getAltNotificationBox(browser, notificationValue);
  1409.     
  1410.     var label = this.getString("ABE.notify", [info.request, info.lastRule.destinations, info.lastPredicate]);
  1411.     
  1412.     if (!(box && box.appendNotification)) {
  1413.       if (!this.supportsNotifications && this.ns.getPref("ABE.legacyPrompt")) {
  1414.         var prompter = noscriptUtil.prompter;
  1415.         if (prompter.confirmEx(
  1416.           window,
  1417.           "NoScript - Application Boundary Enforcer",
  1418.           label,
  1419.           prompter.BUTTON_POS_0 * prompter.BUTTON_TITLE_IS_STRING |
  1420.           prompter.BUTTON_POS_1 * prompter.BUTTON_TITLE_OK,
  1421.           this.getString("notify.options").replace(this.getString("notify.accessKey"), "&$&"),
  1422.           "", "", null, { value: false }
  1423.         ) == 0) noscriptUtil.openABEOptions(info);
  1424.       }
  1425.       return false;
  1426.     }
  1427.     var notification = null;
  1428.     
  1429.     
  1430.     var icon = this.getIcon("noscript-abe-opts");
  1431.  
  1432.     notification = box.appendNotification(
  1433.       label, 
  1434.       notificationValue, 
  1435.       icon, 
  1436.       box.PRIORITY_WARNING_HIGH,
  1437.       [{
  1438.           label: this.getString("notify.options"),
  1439.           accessKey: this.getString("notify.accessKey"),
  1440.           callback: function(notification, buttonInfo) {
  1441.             noscriptUtil.openABEOptions(info);
  1442.           }
  1443.        }]
  1444.       );
  1445.     browser.addEventListener("beforeunload", function(ev) {
  1446.       if (ev.originalTarget == info.document || ev.originalTarget == browser) {
  1447.         browser.removeEventListener(ev.type, arguments.callee, false);
  1448.         if (notification && notification == box.currentNotification) {
  1449.           box.removeCurrentNotification();
  1450.         } 
  1451.         info = browser = notification = null;
  1452.       }
  1453.     }, false);
  1454.     
  1455.     return true;
  1456.   },
  1457.   
  1458.   
  1459.   unsafeReload: function() {
  1460.     const browser = this.currentBrowser;
  1461.     const ns = this.ns;
  1462.     const rw = ns.requestWatchdog;
  1463.     var unsafeRequest = rw.getUnsafeRequest(browser);
  1464.     var method;
  1465.     if (!unsafeRequest) {
  1466.       unsafeRequest = {
  1467.         URI: browser.webNavigation.currentURI,
  1468.         origin: ns.__parent__.OriginTracer.traceBackHistory(browser.webNavigation.sessionHistory, browser.contentWindow).join(">>>")
  1469.       };
  1470.       method = "URL";
  1471.     } else {
  1472.       method = (unsafeRequest.postData ? "POST" : "GET");
  1473.     }
  1474.     var msg = noscriptUtil.getString("unsafeReload.warning",
  1475.       [ method, 
  1476.         ns.siteUtils.crop(unsafeRequest.URI.spec), 
  1477.         ns.siteUtils.crop(unsafeRequest.origin || unsafeRequest.referrer && unsafeRequest.referrer.spec || '?')
  1478.       ]);
  1479.     msg += noscriptUtil.getString("confirm");
  1480.     if (noscriptUtil.confirm(msg, "confirmUnsafeReload")) {
  1481.       try {
  1482.         getBrowser().getNotificationBox(browser).removeAllNotifications(true);
  1483.       } catch(e) {}
  1484.       rw.unsafeReload(browser, true);
  1485.     }
  1486.   },
  1487.   
  1488.   notificationHide: function(browser) { // Modified by Higmmer
  1489.     var box = this.getNotificationBox(null, browser);
  1490.     var widget = this.getNsNotification(box); // Modified by Higmmer
  1491.     if (widget) {
  1492.       if (box._timer) clearTimeout(box._timer);
  1493.       if (widget.close) {
  1494.         if (box.currentNotification == widget) {
  1495.           box.currentNotification = null;
  1496.         }
  1497.         widget.close();
  1498.         box.style.width = "";
  1499.         window.setTimeout(function() {
  1500.           box.style.width = "100%"
  1501.           window.setTimeout(function() {
  1502.             box.style.width = "";
  1503.           }, 10);
  1504.         }, 10);
  1505.       } else {
  1506.         widget.setAttribute("hidden", "true");
  1507.       }
  1508.       return true;
  1509.     }
  1510.     return false;
  1511.   },
  1512.   
  1513.   get _oldStylePartial() {
  1514.     delete this._oldStylePartial;
  1515.     return this._oldStylePartial = this.ns.getPref("oldStylePartial", false);
  1516.   },
  1517.   
  1518.   _syncUINow: function() {
  1519.     
  1520.     const ns = this.ns;
  1521.     const global = ns.jsEnabled;
  1522.     const jsPSs = ns.jsPolicySites;
  1523.     const untrustedSites = ns.untrustedSites;
  1524.     var lev;
  1525.     
  1526.     this.syncXssWidget();
  1527.     this.syncRedirectWidget();
  1528.     
  1529.     const sites = this.getSites();
  1530.     
  1531.     const oldStylePartial = this._oldStylePartial;
  1532.     
  1533.     if (this._currentPopup && this._currentPopup.getAttribute("sticky") == "true" && this._currentPopup.state == "open") {
  1534.       this.prepareMenu(this._currentPopup, sites);
  1535.     }
  1536.     
  1537.     var totalScripts = sites.scriptCount;
  1538.     var totalPlugins = sites.pluginCount;
  1539.     var totalAnnoyances = totalScripts + totalPlugins;
  1540.     var notificationNeeded = false;
  1541.     var allowedSites = [];
  1542.     var activeSites = sites.pluginSites.concat(sites.docSites);
  1543.     var allowed = 0;
  1544.     var untrusted = 0;
  1545.     var active = 0;
  1546.     var blockedObjects = 0;
  1547.     var isUntrusted = false;
  1548.     var topTrusted = false;
  1549.     var topUntrusted = false;
  1550.     
  1551.     if (global && !ns.alwaysBlockUntrustedContent) {
  1552.       lev = "glb";
  1553.     } else {
  1554.       var s = sites.length;
  1555.       if (sites.pluginExtras) sites.pluginExtras.forEach(function(pe) { blockedObjects += pe.filter(function(e) { return e && e.placeholder; }).length });
  1556.       var total = s + blockedObjects;
  1557.      
  1558.       var url, site;
  1559.       while (s-- > 0) {
  1560.         url = sites[s];
  1561.         isUntrusted = untrustedSites.matches(url);
  1562.         site = !isUntrusted && (global ? url : jsPSs.matches(url));
  1563.         
  1564.         if (site && url == sites.topURL) {
  1565.           if (this.currentBrowser.webNavigation.allowJavascript) topTrusted = true;
  1566.           else {
  1567.             site = null;
  1568.             if (isUntrusted) topUntrusted = true;
  1569.           }
  1570.         }
  1571.         
  1572.         if (site) {
  1573.           if (oldStylePartial || activeSites.indexOf(url) > -1) active++;
  1574.           if (ns.isMandatory(site) || allowedSites.indexOf(site) > -1) {
  1575.             total--;
  1576.           } else {
  1577.             allowedSites.push(site);
  1578.           }
  1579.         } else {
  1580.           if (isUntrusted) untrusted++;
  1581.           else if(!notificationNeeded && url != "about:blank") {
  1582.             notificationNeeded = true;
  1583.           }
  1584.         }
  1585.       }
  1586.       allowed = allowedSites.length;
  1587.       lev = (allowed == total && sites.length > 0 && !untrusted) ? (global ? "glb" : "yes")
  1588.             : allowed == 0 || active == 0 ? (global ? "untrusted-glb" : topUntrusted ? "untrusted" : "no") 
  1589.             : (untrusted > 0 && !(notificationNeeded || blockedObjects) ? (global ? "yu-glb" : "yu") 
  1590.                : topTrusted ? "prt" : "subprt");
  1591.       notificationNeeded = notificationNeeded && totalAnnoyances > 0;
  1592.     }
  1593.     
  1594.     var message = this.getString("allowed." +
  1595.         (lev == "yu" || lev == "subprt" ? "prt" : lev == "untrusted" ? "no" : lev));
  1596.     
  1597.     var shortMessage = message.replace(/JavaScript/g, "JS");
  1598.     
  1599.     if (notificationNeeded && active) 
  1600.       message += ", " + allowed + "/" + total + " (" + allowedSites.join(", ") + ")";
  1601.     
  1602.     var countsMessage = " | <SCRIPT>: " + totalScripts + " | <OBJECT>: " + totalPlugins;
  1603.     message += countsMessage;
  1604.     shortMessage += countsMessage;
  1605.     
  1606.     var icon = this.getIcon(this.statusIcon);
  1607.     var className = this.getStatusClass(lev, !totalAnnoyances);
  1608.     
  1609.     var widget = $("noscript-tbb");
  1610.     if (widget) {
  1611.       widget.setAttribute("tooltiptext", shortMessage);
  1612.       this.updateStatusClass(widget, className); 
  1613.     }
  1614.     
  1615.     widget = this.statusIcon;
  1616.     widget.setAttribute("tooltiptext", shortMessage);
  1617.     this.updateStatusClass(widget, className);
  1618.     
  1619.     if (notificationNeeded) { // notifications
  1620.       const win = window.content;
  1621.       if (this.notify) {
  1622.         this.notificationShow(message,
  1623.           this.getIcon(widget), 
  1624.           !(ns.getExpando(win, "messageShown") && this.notifyHidePermanent));
  1625.         ns.setExpando(win, "messageShown", true);
  1626.       } else {
  1627.         this.notificationHide(); 
  1628.       }
  1629.       if (!ns.getExpando(win, "soundPlayed")) {
  1630.         ns.soundNotify(window.content.location.href);
  1631.         ns.setExpando(win, "soundPlayed");
  1632.       }
  1633.     } else {
  1634.       this.notificationHide();
  1635.       message = shortMessage = "";
  1636.     }
  1637.     
  1638.     widget = $("noscript-statusLabelValue");
  1639.     if (widget) {
  1640.       widget.setAttribute("value", shortMessage);
  1641.       widget.parentNode.style.display = message ? "" : "none";
  1642.     }
  1643.     
  1644.     widget =  $("noscript-tbb-revoke-temp");
  1645.     if (widget) {
  1646.       if (ns.gTempSites.sitesString || ns.tempSites.sitesString || ns.objectWhitelistLen || ns.clearClickHandler && ns.clearClickHandler.whitelistLen) {
  1647.         widget.removeAttribute("disabled");
  1648.       } else {
  1649.         widget.setAttribute("disabled", "true");
  1650.       }
  1651.     }
  1652.     
  1653.     widget =  $("noscript-tbb-temp-page");
  1654.     if (widget) {
  1655.       if (allowed < total) {
  1656.         widget.removeAttribute("disabled");
  1657.       } else {
  1658.         widget.setAttribute("disabled", "true");
  1659.       }
  1660.     }
  1661.   }
  1662. ,
  1663.   notifyHideTimeout: 0,
  1664.   liveReload: false,
  1665.   
  1666.   initContentWindow: function(window) {
  1667.     window.addEventListener("pagehide", this.listeners.onPageHide, true);
  1668.   },
  1669.   
  1670.   cleanupDocument: function(doc, browser) {
  1671.     
  1672.     if (!(doc.defaultView && doc.defaultView == doc.defaultView.top)) return;
  1673.     
  1674.     const ns = this.ns;
  1675.     browser = browser || ns.dom.findBrowserForNode(doc);
  1676.     if (browser) {
  1677.       ns.setExpando(browser, "pe", null);
  1678.     }
  1679.   },
  1680.   
  1681.   prefsObserver: {
  1682.     ns: noscriptUtil.service,
  1683.     QueryInterface: noscriptUtil.service.generateQI([
  1684.         CI.nsISupports, 
  1685.         CI.nsIObserver, 
  1686.         CI.nsISupportsWeakReference])
  1687.   ,
  1688.     observe: function(subject, topic, data) {
  1689.       if (subject == this.ns.caps) {
  1690.          noscriptOverlay.syncUI();
  1691.          return;
  1692.       }
  1693.       switch (data) {
  1694.         case "statusIcon": case "statusLabel":
  1695.           window.setTimeout(function() {
  1696.               var widget =$("noscript-" + data);
  1697.               if (widget) {
  1698.                 widget.setAttribute("hidden", !noscriptOverlay.ns.getPref(data))
  1699.               }
  1700.             }, 0);
  1701.         break;
  1702.         
  1703.         case "notify":
  1704.         case "notify.bottom":
  1705.           noscriptOverlay[data.replace(/\.b/, 'B')] = this.ns.getPref(data);
  1706.           if(this._registered) noscriptOverlay.notificationHide();
  1707.         break;
  1708.         
  1709.         case "keys.ui":
  1710.         case "keys.toggle":
  1711.           noscriptOverlay.shortcutKeys.setup(data.replace(/^keys\./, ""), this.ns.getPref(data, ""));
  1712.         break;
  1713.         
  1714.         case "notify.hidePermanent":
  1715.         case "notify.hideDelay":
  1716.         case "notify.hide":
  1717.           noscriptOverlay[data.replace(/\.h/, 'H')] = this.ns.getPref(data);
  1718.         break;
  1719.         
  1720.         case "stickyUI.liveReload":
  1721.           noscriptOverlay.liveReload = this.ns.getPref(data);
  1722.         break;
  1723.       }
  1724.     },
  1725.     _registered: false,
  1726.     register: function() {
  1727.       this.ns.prefs.addObserver("", this, true);
  1728.       this.ns.caps.addObserver("", this, true);
  1729.       const initPrefs = [
  1730.         "statusIcon", "statusLabel", 
  1731.         "keys.ui", "keys.toggle",
  1732.         "notify", "notify.bottom",
  1733.         "notify.hide", "notify.hidePermanent", "notify.hideDelay",
  1734.         "stickyUI.liveReload"
  1735.         ];
  1736.       for (var j = 0; j < initPrefs.length; j++) {
  1737.         this.observe(null, null, initPrefs[j]);
  1738.       }
  1739.       this._registered = true;
  1740.     },
  1741.     remove: function() {
  1742.       this.ns.prefs.removeObserver("", this);
  1743.       this.ns.caps.removeObserver("", this);
  1744.     }
  1745.   },
  1746.   
  1747.   
  1748.   
  1749.   
  1750.   
  1751.   shortcutKeys: {
  1752.     
  1753.     execute: function(cmd, ev) {
  1754.       switch (cmd) {
  1755.         case 'toggle':
  1756.           noscriptOverlay.toggleCurrentPage(noscriptOverlay.ns.preferredSiteLevel);
  1757.         break;
  1758.         case 'ui':
  1759.           noscriptOverlay.showUI()
  1760.         break;
  1761.       }
  1762.     },
  1763.     
  1764.     keys: {},
  1765.     setup: function(name, values) { 
  1766.       values = values.toLowerCase().replace(/^\s*(.*?)\s*$/g, "$1").split(/\s+/);
  1767.       var vpos = values.length;
  1768.       if (vpos) {
  1769.         
  1770.         var mods = { shiftKey: false, altKey: false, metaKey: false, ctrlKey: false };
  1771.         
  1772.         var keyVal = values[--vpos];
  1773.         for (var value; vpos-- > 0;) {
  1774.           value = values[vpos] + "Key";
  1775.           if (value in mods) {
  1776.             mods[value] = true;
  1777.           }
  1778.         }
  1779.         
  1780.         var key = { modifiers: mods, charCode: 0, keyCode: 0 };
  1781.         
  1782.         if (keyVal.length > 3) {
  1783.           var pos = keyVal.indexOf('.');
  1784.           if (pos > 3) {
  1785.             key.charCode = keyVal.charCodeAt(pos + 1) || 0;
  1786.             keyVal = keyVal.substring(0, pos);
  1787.           }
  1788.           key.keyCode = KeyEvent["DOM_" + keyVal.toUpperCase()] || 0;
  1789.         } else {
  1790.           key.charCode = (key.modifiers.shiftKey ? keyVal.toUpperCase() : keyVal).charCodeAt(0) || 0;
  1791.         }
  1792.         
  1793.         this.keys[name] = key;
  1794.       } else {
  1795.         delete(this.keys[name]);
  1796.       }
  1797.     },
  1798.     
  1799.     listener: function(ev) {
  1800.       const binding = arguments.callee.binding;
  1801.       const skk = binding.keys;
  1802.       var cmd, k, p, sk, mods;
  1803.       for (k in skk) {
  1804.         cmd = k;
  1805.         sk = skk[k];
  1806.         var which = ev.which;
  1807.          
  1808.         if (ev.charCode && ev.charCode == sk.charCode || ev.keyCode && ev.keyCode == sk.keyCode) {
  1809.           mods = sk.modifiers;
  1810.           for (p in mods) {
  1811.             if (ev[p] != mods[p]) {
  1812.               cmd = null;
  1813.               break;
  1814.             }
  1815.           }
  1816.           
  1817.           
  1818.           if (cmd) {
  1819.             ev.preventDefault();
  1820.             binding.execute(cmd, ev);
  1821.             return;
  1822.           }
  1823.         }
  1824.       }
  1825.     },
  1826.     
  1827.     
  1828.     
  1829.     register: function() {
  1830.       this.listener.binding = this;
  1831.       window.addEventListener("keypress", this.listener, true);
  1832.     },
  1833.     remove: function() {
  1834.       window.removeEventListener("keypress", this.listener, true);
  1835.     }
  1836.   },
  1837.   
  1838.   hideObject: function(p, o) {
  1839.     if (!p.mimeRx.test(o.type)) return;
  1840.     
  1841.     var r = p.document.createElement("object");
  1842.     r.style.width = o.offsetWidth + "px";
  1843.     r.style.height = o.offsetHeight + "px";
  1844.     r.style.display = "inline-block";
  1845.     o.className += " " + p.className;
  1846.     o.parentNode.insertBefore(r, o);
  1847.   },
  1848.   
  1849.   showObject: function(p, o) {
  1850.     var cs = o.className;
  1851.     cs = cs.replace(p.classRx, '');
  1852.     if (cs != o.className) {
  1853.       o.className = cs;
  1854.       var r = o.previousSibling;
  1855.       if (r instanceof HTMLObjectElement) {
  1856.         r.parentNode.removeChild(r);
  1857.       }
  1858.     }
  1859.   },
  1860.   
  1861.   _tags: ["object", "embed"],
  1862.   toggleObjectsVisibility: function(d, v) {
  1863.     var ns = noscriptOverlay.ns;
  1864.     var rx = ns.hideOnUnloadRegExp;
  1865.     if (!rx) return;
  1866.     var callback = v ? noscriptOverlay.showObject : noscriptOverlay.hideObject;
  1867.     var params = {
  1868.       document: d,
  1869.       mimeRx: rx,
  1870.       classRx: noscriptOverlay.ns.hideObjClassNameRx,
  1871.       className: ns.hideObjClassName
  1872.     };
  1873.     var aa = null;
  1874.     var j;
  1875.     for each(var t in this._tags) {
  1876.       var oo = d.getElementsByTagName(t);
  1877.       j = oo.length;
  1878.       if (j) {
  1879.         aa = aa || [oo[--j]];
  1880.         while(j-- > 0) {
  1881.           aa.push(oo[j]);
  1882.         }
  1883.       }
  1884.     }
  1885.     if (aa) {
  1886.       for (j = aa.length; j-- > 0;) {
  1887.         callback(params, aa[j]);
  1888.       }
  1889.     }
  1890.   },
  1891.   
  1892.   listeners: {
  1893.     
  1894.     onTabClose: function(ev) {
  1895.       try {
  1896.         var browser = ev.target.linkedBrowser;
  1897.         noscriptOverlay.ns.cleanupBrowser(browser);
  1898.         
  1899.         var tabbrowser = getBrowser();
  1900.         if (tabbrowser._browsers) tabbrowser._browsers = null;
  1901.         if (tabbrowser.getNotificationBox) {
  1902.           tabbrowser.getNotificationBox(browser).removeAllNotifications(true);
  1903.         }
  1904.       } catch(e) {}
  1905.     },
  1906.     
  1907.     webProgressListener: {
  1908.       QueryInterface: noscriptUtil.service.generateQI([CI.nsIWebProgressListener]),
  1909.       STATE_STOP: CI.nsIWebProgressListener.STATE_STOP,
  1910.       onLocationChange: function(aWebProgress, aRequest, aLocation) {
  1911.         const domWindow = aWebProgress.DOMWindow;
  1912.         if (domWindow) {
  1913.           noscriptOverlay.syncUI(domWindow);
  1914.         }
  1915.       },
  1916.       onStatusChange: function() {}, 
  1917.       onStateChange: function(aWebProgress, aRequest, stateFlags, status) {
  1918.         if (stateFlags & this.STATE_STOP) {
  1919.           const domWindow = aWebProgress.DOMWindow;
  1920.           if (domWindow == domWindow.top) {
  1921.             noscriptOverlay.syncUI(domWindow);
  1922.           }
  1923.         } 
  1924.       }, 
  1925.       onSecurityChange: function() {}, 
  1926.       onProgressChange: function() {}
  1927.     },
  1928.     
  1929.     onContentLoad: function(ev) {
  1930.  
  1931.       var doc = ev.originalTarget;
  1932.       if (doc instanceof HTMLDocument) {
  1933.         var w = doc.defaultView;
  1934.         if (w) {
  1935.           const ns = noscriptOverlay.ns;
  1936.           noscriptOverlay.ns.setExpando(w, "contentLoaded", true);
  1937.           if (w == w.top) {
  1938.             ns.processMetaRefresh(doc, noscriptOverlay.notifyMetaRefreshCallback);
  1939.             if (w == window.content) {
  1940.               noscriptOverlay._syncUINow();
  1941.             }
  1942.           } else {
  1943.             ns.frameContentLoaded(w);
  1944.             noscriptOverlay.syncUI(w.top);
  1945.           }
  1946.           w.addEventListener("load", noscriptOverlay.listeners.onDocumentLoad, false);
  1947.         }
  1948.       }
  1949.     },
  1950.     onDocumentLoad: function(ev) {
  1951.       if (ev.originalTarget instanceof HTMLDocument) {
  1952.         var w = ev.currentTarget;
  1953.         w.removeEventListener("load", arguments.callee, false);
  1954.         
  1955.         window.setTimeout(function() {
  1956.           noscriptOverlay.ns.detectJSRedirects(w.document);
  1957.         }, 0);
  1958.       }
  1959.     },
  1960.     
  1961.     onPageShow: function(ev) {
  1962.       
  1963.       if (ev.persisted && (ev.target instanceof HTMLDocument)) {
  1964.         var d = ev.target;
  1965.         var ns = noscriptOverlay.ns;
  1966.         noscriptOverlay.toggleObjectsVisibility(d, true);
  1967.       }
  1968.       noscriptOverlay.syncUI();
  1969.     },
  1970.     onPageHide: function(ev) {
  1971.       var d = ev.target;
  1972.       if (d instanceof HTMLDocument) {
  1973.         var ns = noscriptOverlay.ns;
  1974.         noscriptOverlay.toggleObjectsVisibility(d, false);
  1975.       }
  1976.     },
  1977.     
  1978.     onMainContextMenu:  function(ev) { noscriptOverlay.prepareContextMenu(ev) },
  1979.     
  1980.     onLoad: function(ev) {
  1981.       window.removeEventListener("load", arguments.callee, false);
  1982.       window.addEventListener("unload", noscriptOverlay.listeners.onUnload, false);
  1983.       try {
  1984.         noscriptOverlay.listeners.setup(); 
  1985.         noscriptOverlay.wrapBrowserAccess();
  1986.         var hacks = noscriptOverlay.Hacks;
  1987.         hacks.torButton();
  1988.         window.setTimeout(hacks.pdfDownload, 0);
  1989.         noscriptOverlay.initPopups();
  1990.       } catch(e) {
  1991.         var msg = "[NoScript] Error initializing new window " + e + "\n"; 
  1992.         noscriptOverlay.ns.log(msg);
  1993.         noscriptOverlay.ns.dump(msg);
  1994.       }
  1995.     },
  1996.     onUnload: function(ev) {
  1997.       window.removeEventListener("unload", arguments.callee, false);
  1998.       noscriptOverlay.listeners.teardown();
  1999.       window.browserDOMWindow = null;
  2000.       noscriptOverlay.dispose();
  2001.     },
  2002.     
  2003.     setup: function() {
  2004.       
  2005.       var context = $("contentAreaContextMenu");
  2006.       if (!context) return; // not a browser window?
  2007.       
  2008.       context.addEventListener("popupshowing", this.onMainContextMenu, false);
  2009.       
  2010.       var b = getBrowser();
  2011.         
  2012.       b.addProgressListener(this.webProgressListener, CI.nsIWebProgress.NOTIFY_STATE_WINDOW | CI.nsIWebProgress.NOTIFY_LOCATION);
  2013.   
  2014.       if (b.tabContainer) {
  2015.         b.tabContainer.addEventListener("TabClose", this.onTabClose, false);
  2016.       }
  2017.       
  2018.       window.addEventListener("DOMContentLoaded", this.onContentLoad, false);
  2019.       
  2020.       
  2021.       window.addEventListener("pageshow", this.onPageShow, true);
  2022.       window.addEventListener("pagehide", this.onPageHide, true);
  2023.  
  2024.       noscriptOverlay.shortcutKeys.register();
  2025.       noscriptOverlay.prefsObserver.register();
  2026.  
  2027.       noscriptUtil.service.checkVersion();
  2028.  
  2029.     },
  2030.     
  2031.     
  2032.    
  2033.     teardown: function() {
  2034.  
  2035.       var b = getBrowser();
  2036.       if (b) {
  2037.         b.removeEventListener("click", this.onBrowserClick, true);
  2038.         if (b.tabContainer) {
  2039.           b.tabContainer.removeEventListener("TabClose", this.onTabClose, false);
  2040.         }
  2041.         
  2042.         b.removeProgressListener(this.webProgressListener);
  2043.       }
  2044.       
  2045.       window.removeEventListener("pagehide", this.onPageHide, true);
  2046.       window.removeEventListener("pageshow", this.onPageShow, true);
  2047.       window.removeEventListener("DOMContentLoaded", this.onContentLoad, false);
  2048.  
  2049.       noscriptOverlay.prefsObserver.remove();
  2050.       noscriptOverlay.shortcutKeys.remove();
  2051.       
  2052.      $("contentAreaContextMenu").removeEventListener("popupshowing", this.onMainContextMenu, false);
  2053.       
  2054.     }
  2055.     
  2056.   }, // END listeners
  2057.   
  2058.   
  2059.   get _browserReady() {
  2060.     return window.gBrowser || window.Browser && (("browsers" in Browser) || Browser._canvasBrowser || Browser._content);
  2061.   },
  2062.   get currentBrowser() {
  2063.     if (!this._browserReady) return null;
  2064.     delete this.currentBrowser;
  2065.     this.__defineGetter__("currentBrowser",
  2066.       window.gBrowser && function() { return gBrowser.selectedBrowser; }
  2067.       || Browser.selectedBrowser && function() { return Browser.selectedBrowser; }
  2068.       || Browser.currentBrowser && function() { return Browser.currentBrowser; }
  2069.     );
  2070.     return this.currentBrowser;
  2071.   },
  2072.   
  2073.   get browsers() {
  2074.     if (!this._browserReady) return [];
  2075.     delete this.browsers;
  2076.     var browsersContainer = window.Browser // Fennec
  2077.         ? ("browsers" in Browser) && Browser || Browser._canvasBrowser || Browser._content  
  2078.         : window.gBrowser; // desktop Firefox
  2079.  
  2080.     this.__defineGetter__("browsers", function() { return browsersContainer.browsers; });
  2081.      
  2082.     if ("Browser" in window && window.Browser._content) { // Fennec Alpha 1
  2083.       getBrowserForDisplay = function() { Browser._content.getBrowserForDisplay.apply(Browser._content, arguments); };
  2084.     }
  2085.     return this.browsers;
  2086.   },
  2087.   
  2088.   isBrowserEnabled: function(browser) {
  2089.     browser = browser || this.currentBrowser;
  2090.     return browser.docShell.allowJavascript;
  2091.   },
  2092.  
  2093.   
  2094.   wrapBrowserAccess: function() { // called onload
  2095.     if (!window.nsBrowserAccess) {
  2096.       noscriptOverlay.ns.log("[NoScript] nsBrowserAccess not found?!");
  2097.       return;
  2098.     }
  2099.   
  2100.     if (!nsBrowserAccess.prototype.wrappedJSObject) {
  2101.       nsBrowserAccess.prototype.__defineGetter__("wrappedJSObject", noscriptOverlay.browserAccess.self);
  2102.     }
  2103.     
  2104.     if (!(window.browserDOMWindow && browserDOMWindow.wrappedJSObject && (browserDOMWindow.wrappedJSObject instanceof nsBrowserAccess))) {
  2105.       if (!'retryCount' in arguments.callee) {
  2106.         arguments.callee.retryCount = 10;
  2107.       } else if (arguments.callee.retryCount) {
  2108.         noscriptOverlay.ns.log("[NoScript] browserDOMWindow not found or not set up, retrying " + arguments.callee.retryCount + " times");
  2109.         arguments.callee.retryCount--;
  2110.       }
  2111.       window.setTimeout(arguments.callee, 0);
  2112.       return;
  2113.     }
  2114.     
  2115.     browserDOMWindow.wrappedJSObject.openURI = noscriptOverlay.browserAccess.openURI;
  2116.     
  2117.     if(noscriptOverlay.ns.consoleDump) 
  2118.       noscriptOverlay.ns.dump("browserDOMWindow wrapped for external load interception");
  2119.   },
  2120.   
  2121.   browserAccess: {
  2122.     self: function() { return this; },
  2123.     openURI: function(aURI, aOpener, aWhere, aContext) {
  2124.       const ns = noscriptUtil.service;
  2125.  
  2126.       var external = aContext == CI.nsIBrowserDOMWindow.OPEN_EXTERNAL && aURI;
  2127.       if (external) {
  2128.         if (aURI.schemeIs("http") || aURI.schemeIs("https")) {
  2129.            // remember for filter processing
  2130.            ns.requestWatchdog.externalLoad = aURI.spec;
  2131.         } else {
  2132.            // don't let the external protocol open dangerous URIs
  2133.            if (aURI.schemeIs("javascript") || aURI.schemeIs("data")) {
  2134.              var err = "[NoScript] external non-http load blocked: " + aURI.spec;
  2135.              ns.log(err);
  2136.              throw err;
  2137.            }
  2138.         }
  2139.       }
  2140.       
  2141.       if (aURI && ns.extraCapturedProtocols && ns.extraCapturedProtocols.indexOf(aURI.scheme) > -1) {
  2142.         return aOpener || window.content;
  2143.       }
  2144.       
  2145.       var w = null;
  2146.       try {
  2147.         w = nsBrowserAccess.prototype.openURI.apply(this, arguments);
  2148.         if (external && ns.consoleDump) ns.dump("[NoScript] external load intercepted");
  2149.       } finally {
  2150.         if (external && !w) ns.requestWatchdog.externalLoad = null;
  2151.       }
  2152.       return w;
  2153.     }
  2154.   },
  2155.   
  2156.   Hacks: {
  2157.   
  2158.     pdfDownload: function() {
  2159.       if (typeof(mouseClick) != "function") return;
  2160.       var tb = getBrowser();
  2161.       tb.removeEventListener("click", mouseClick, true);
  2162.       tb.addEventListener("click", mouseClick, false);
  2163.     },
  2164.     
  2165.     torButton: function() {
  2166.       if ("torbutton_update_tags" in window && typeof(window.torbutton_update_tags) == "function") {
  2167.         // we make TorButton aware that we could have a part in suppressing JavaScript on the browser
  2168.         noscriptOverlay.ns.log("TB: " + window.torbutton_update_tags);
  2169.         window.eval(
  2170.           window.torbutton_update_tags.toSource().replace(/\bgetBoolPref\("javascript\.enabled"\)/g,
  2171.           "$& && (!noscriptOverlay || noscriptOverlay.isBrowserEnabled(browser))"));
  2172.         noscriptOverlay.ns.log("Patched TB: " + window.torbutton_update_tags);
  2173.       }
  2174.     }
  2175.   },
  2176.   
  2177.   install: function() {
  2178.     // this.ns.dump("*** OVERLAY INSTALL ***\n");
  2179.     this.ns.setPref("badInstall", false);
  2180.     this.ns.dom._winType = document.documentElement.getAttribute("windowtype");
  2181.     window.addEventListener("load", this.listeners.onLoad, false);
  2182.   },
  2183.  
  2184.   dispose: function() {
  2185.  
  2186.     for (var bb = this.browsers, j = bb.length; j-- > 0;) {
  2187.       try {
  2188.         this.cleanupDocument(bb[j].contentWindow.document, bb);
  2189.       } catch(e) {
  2190.         this.ns.dump(e);
  2191.       }
  2192.       this.ns.cleanupBrowser(bb[j]);
  2193.     }
  2194.     // this.ns.dump("*** OVERLAY DISPOSE ***\n");
  2195.   }
  2196. }
  2197. : {
  2198.   install: function() {
  2199.     window.addEventListener("load", function(ev) {
  2200.       ev.currentTarget.removeEventListener("load", arguments.callee, false); 
  2201.       var node = null;
  2202.       for each(var id in ["noscript-context-menu", "noscript-tbb", "noscript-statusIcon"]) {
  2203.         node = $(id);
  2204.         if (node) node.hidden = true;
  2205.       }
  2206.       node = null;
  2207.       var prefs = this.prefService = CC["@mozilla.org/preferences-service;1"]
  2208.         .getService(CI.nsIPrefService).getBranch("noscript.");
  2209.       try {
  2210.         if (prefs.getBoolPref("badInstall")) return;
  2211.       } catch(e) {}
  2212.       prefs.setBoolPref("badInstall", true);
  2213.       prefs = null;
  2214.       window.setTimeout(function() {
  2215.         alert("NoScript is not properly installed and cannot operate correctly.\n" + 
  2216.               "Please install it again and check the Install FAQ section on http://noscript.net/faq if this problem persists.");
  2217.         noscriptUtil.browse("http://noscript.net/faq#faqsec2", null);
  2218.           
  2219.       },10);
  2220.     }, false);
  2221.   }
  2222. }})()
  2223.  
  2224. noscriptOverlay.install();
  2225.